package so.wy.servicecenter.businessbed.service.rent;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import so.wdl.framework.core.date.DateUnit;
import so.wdl.framework.core.date.DateUtil;
import so.wy.servicecenter.base.exception.ManagerException;
import so.wy.servicecenter.base.exception.ServiceException;
import so.wy.servicecenter.base.frame.service.AbstractService;
import so.wy.servicecenter.businessbed.dao.enums.StatusOrderEnum;
import so.wy.servicecenter.businessbed.dao.enums.StatusOrderPayEnum;
import so.wy.servicecenter.businessbed.dao.rdb.device.DeviceDO;
import so.wy.servicecenter.businessbed.dao.rdb.hospital.hospital_price.HospitalPriceDO;
import so.wy.servicecenter.businessbed.dao.rdb.order.order.OrderDAO;
import so.wy.servicecenter.businessbed.dao.rdb.order.order.OrderDO;
import so.wy.servicecenter.businessbed.dao.rdb.order.order_price.OrderPriceDAO;
import so.wy.servicecenter.businessbed.dao.rdb.order.order_price.OrderPriceDO;
import so.wy.servicecenter.businessbed.dao.rdb.rent_user.RentUserDO;
import so.wy.servicecenter.businessbed.manager.inner.DeviceIManager;
import so.wy.servicecenter.businessbed.manager.inner.HospitalIManager;
import so.wy.servicecenter.businessbed.manager.inner.RentUserIManager;
import so.wy.servicecenter.businessbed.manager.normal.WxMiniMsgManager;
import so.wy.servicecenter.businessbed.manager.normal.WxPayOptManager;
import so.wy.servicecenter.businessbed.manager.utils.OrderUtils;

import java.math.BigDecimal;
import java.util.Date;

@Service
@Scope("prototype")
public class OrderOptService extends AbstractService {
    @Autowired
    private DeviceIManager deviceIManager;

    @Autowired
    private RentUserIManager rentUserIManager;

    @Autowired
    private HospitalIManager hospitalIManager;

    @Autowired
    private WxPayOptManager wxPayOptManager;

    @Autowired
    private WxMiniMsgManager wxMiniMsgManager;

    @Autowired
    private OrderDAO orderDAO;

    @Autowired
    private OrderPriceDAO orderPriceDAO;

    //租赁
    @Transactional(rollbackFor = Exception.class)
    public String rentDevice(Long userId, String deviceSerial) throws ServiceException {
        //此处为了防止租户重复调用应该枷锁
        RentUserDO rentUserDO;
        DeviceDO deviceDO;
        HospitalPriceDO priceDO;
        try {
            //校验租户是否存在并且是否可租赁
            rentUserDO = rentUserIManager.checkCanOrder(userId);

            //校验床是否存在并且是否可租赁
            deviceDO = deviceIManager.checkDeviceCanOrder(deviceSerial);

            //校验医院是否可租赁
            priceDO = hospitalIManager.checkHospitalCanOrder(deviceDO.getHospitalId());
        } catch (ManagerException exception) {
            throw exception(exception.getMessage());
        }
        //进行订单信息填充
        OrderDO orderDO = new OrderDO();
        orderDO.setOrderSerial(OrderUtils.genOrderSerial(userId));
        orderDO.setOrderTime(new Date());
        orderDO.setDeviceSerial(deviceSerial);
        orderDO.setRentUserId(rentUserDO.getRentUserId());
        orderDO.setHospitalId(deviceDO.getHospitalId());
        orderDO.setBuildingId(deviceDO.getBuildingId());
        orderDO.setFloorId(deviceDO.getFloorId());
        orderDO.setDepartId(deviceDO.getDepartId());
        orderDO.setOrderStatus(StatusOrderEnum.PROCESSING.getCode());
        orderDO.setPayStatus(StatusOrderPayEnum.UNPAID.getCode());
        orderDO.setPriceDesc(priceDO.getPriceDesc());
        orderDO.setRentUserName(rentUserDO.getRentUserName());
        orderDO.setUserPhone(rentUserDO.getRentUserPhone());
        orderDO.setHospitalName(deviceDO.getHospitalName());
        orderDO.setBuildingName(deviceDO.getBuildingName());
        orderDO.setFloorName(deviceDO.getFloorName());
        orderDO.setDepartName(deviceDO.getDepartName());
        orderDAO.insert(orderDO);

        //进行订单价格信息设置
        OrderPriceDO orderPriceDO = new OrderPriceDO();
        orderPriceDO.setOrderSerial(orderDO.getOrderSerial());
        orderPriceDO.setPriceType(priceDO.getPriceType());
        orderPriceDO.setPriceStartHour(priceDO.getPriceStartHour());
        orderPriceDO.setPriceStartMoney(priceDO.getPriceStartMoney());
        orderPriceDO.setPriceAfterMoney(priceDO.getPriceAfterMoney());
        orderPriceDAO.insert(orderPriceDO);

        //更新设备状态
        deviceIManager.deviceCreateOrder(deviceDO.getDeviceId());

        //推送信息
        wxMiniMsgManager.sendOrderProcessingMsg(rentUserDO, orderDO);

        //返回订单编号
        return orderDO.getOrderSerial();

    }

    //开锁失败
    public void rentError(Long userId, String orderSerial) throws ServiceException {
        try {
            //校验租户是否存在
            RentUserDO rentUserDO = rentUserIManager.checkCanOrder(userId);

            //校验订单是否存在并归属于该租户
            OrderDO orderDO = checkOrderBelongRentUser(orderSerial, rentUserDO.getRentUserId());

            //修改订单为已关闭.并添加备注
            OrderDO upDO = new OrderDO();
            upDO.setOrderId(orderDO.getOrderId());
            upDO.setOrderStatus(StatusOrderEnum.CLOSE.getCode());
            upDO.setOrderRemark("开锁失败，订单关闭");
            orderDAO.updateById(upDO);

            //更新设备状态
            DeviceDO deviceDO = deviceIManager.getDeviceInfo(orderDO.getDeviceSerial());
            deviceIManager.deviceFinishOrder(deviceDO.getDeviceId());

            //推送消息
            //todo 推送消息
        } catch (ManagerException exception) {
            throw exception(exception.getMessage());
        }

    }

    //归还  此处应该是根据用户找寻订单信息  然后将设备定位可租赁状态 不是根据设备查找订单
    @Transactional(rollbackFor = Exception.class)
    public void returnDevice(Long userId, String deviceSerial) throws ServiceException {
        logger.error(deviceSerial+"：开始归还!");
        //此处应该加锁 防止重复操作
        try {
            //校验租户是否存在
            RentUserDO rentUserDO = rentUserIManager.checkRentUserExist(userId);

            //校验设备是否可归还
            DeviceDO deviceDO = deviceIManager.checkDeviceReturnable(deviceSerial);

            //获取订单信息 订单是否归属租户存在
            OrderDO orderDO = checkRentUserRunOrder(rentUserDO.getRentUserId());

            //订单结束
            orderFinishOpt(orderDO);

            //刷新医院信息
            hospitalIManager.orderFinish(orderDO);

            //更新设备状态
            deviceIManager.deviceFinishOrder(deviceDO.getDeviceId());

            //推送消息
            wxMiniMsgManager.sendOrderFinshMsg(rentUserDO, orderDO);


        } catch (ManagerException exception) {
            throw exception(exception.getMessage());
        }

        logger.error(deviceSerial+"：归还成功!");
    }

    //订单预支付
    public WxPayMpOrderResult payPreOrder(Long userId, String orderserial) throws ServiceException {
        try {
            //校验租户是否存在
            RentUserDO rentUserDO = rentUserIManager.checkRentUserExist(userId);

            //获取订单信息
            OrderDO orderDO = checkRentUserPayOrder(orderserial, rentUserDO.getRentUserId());

            //校验订单是否归属该租户
            checkOrderBelongRentUser(orderDO.getOrderSerial(), rentUserDO.getRentUserId());

            //校验订单是否可支付
            if (!StatusOrderEnum.FINISH.getCode().equals(orderDO.getOrderStatus())) {
                throw exception("订单信息异常");
            }

            //生成预支付信息
            return wxPayOptManager.createOrderPrePay(rentUserDO, orderDO);
        } catch (ManagerException exception) {
            throw exception(exception.getMessage());
        }
    }

    //人工结束订单
    @Transactional(rollbackFor = Exception.class)
    public void finishOrder(Long userId, String orderSerial) throws ServiceException {
        OrderDO orderDO = checkOrderProcessing(orderSerial);

        DeviceDO deviceDO = deviceIManager.getDeviceInfo(orderDO.getDeviceSerial());

        orderFinishOpt(orderDO);

        //刷新医院信息
        hospitalIManager.orderFinish(orderDO);

        //更新设备状态
        deviceIManager.deviceFinishOrder(deviceDO.getDeviceId());

        //推送消息
        wxMiniMsgManager.sendOrderFinshMsg(rentUserIManager.getRentUserById(orderDO.getRentUserId()), orderDO);
    }


    //人工免单
    public void freeOrder(Long userId, String orderSerial) throws ServiceException {
        OrderDO orderDO = checkOrderExist(orderSerial);

        if (!StatusOrderEnum.FINISH.getCode().equals(orderDO.getOrderStatus())) {
            throw exception("订单信息异常");
        }

        //更新订单信息
        orderDO.setOrderAmountPay(new BigDecimal(0));
        orderDO.setPayStatus(StatusOrderPayEnum.PAID.getCode());
        orderDO.setOrderRemark("免单");
        orderDAO.updateById(orderDO);

        //更新医院订单信息
        hospitalIManager.orderPayFree(orderDO);
    }

    private OrderDO checkRentUserRunOrder( Long rentUserId) throws ServiceException {
        OrderDO orderDO = orderDAO.selectOne(new LambdaQueryWrapper<OrderDO>()
                .eq(OrderDO::getRentUserId, rentUserId)
                .eq(OrderDO::getOrderStatus, StatusOrderEnum.PROCESSING.getCode()));
        if (orderDO == null) {
            throw exception("当前订单信息异常!");
        }
        return orderDO;
    }

    private OrderDO checkRentUserPayOrder(String orderSerial, Long rentUserId) throws ServiceException {
        OrderDO orderDO = orderDAO.selectOne(new LambdaQueryWrapper<OrderDO>()
                .eq(OrderDO::getOrderSerial, orderSerial)
                .eq(OrderDO::getRentUserId, rentUserId)
                .eq(OrderDO::getPayStatus,StatusOrderPayEnum.UNPAID.getCode())
                .eq(OrderDO::getOrderStatus, StatusOrderEnum.FINISH.getCode()));
        if (orderDO == null) {
            throw exception("当前订单信息异常!");
        }
        return orderDO;
    }


    private void orderFinishOpt(OrderDO orderDO) {
        int useLength = (int) DateUtil.between(orderDO.getOrderTime(), new Date(), DateUnit.SECOND);
        BigDecimal orderFee = OrderUtils.calcOrderFee(orderPriceDAO.selectOne(new LambdaQueryWrapper<OrderPriceDO>()
                .eq(OrderPriceDO::getOrderSerial, orderDO.getOrderSerial())), useLength);
        orderDO.setOrderEndTime(new Date());
        orderDO.setUseLength(useLength);
        orderDO.setOrderAmount(orderFee);
        orderDO.setOrderStatus(StatusOrderEnum.FINISH.getCode());
        orderDAO.updateById(orderDO);
    }

    private OrderDO checkOrderProcessing(String orderSerial) throws ServiceException {
        OrderDO orderDO = checkOrderExist(orderSerial);

        if (!StatusOrderEnum.PROCESSING.getCode().equals(orderDO.getOrderStatus())) {
            throw exception("订单信息异常");
        }
        return orderDO;
    }

    private OrderDO checkOrderBelongRentUser(String orderSerial, Long rentUserId) throws ServiceException {
        OrderDO orderDO = orderDAO.selectOne(new LambdaQueryWrapper<OrderDO>().eq(OrderDO::getOrderSerial, orderSerial));
        if (orderDO == null || !orderDO.getRentUserId().equals(rentUserId)) {
            throw exception("订单信息异常");
        }
        return orderDO;
    }

    private OrderDO checkOrderExist(String orderSerial) throws ServiceException {
        OrderDO orderDO = orderDAO.selectOne(new LambdaQueryWrapper<OrderDO>().eq(OrderDO::getOrderSerial, orderSerial));

        if (orderDO == null) {
            throw exception("订单不存在!");
        }

        return orderDO;
    }
}
